﻿using AngleSharp.Dom.Html;
using AngleSharp.Parser.Html;
using DonetSpider;
using DonetSpider.config;
using DonetSpider.Db;
using DonetSpider.http;
using DonetSpider.Log;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

namespace DonetSpider
{
    /// <summary>
    /// 检索结果明细
    /// </summary>
    public class SearchResultItems {
        public string Title { get; set; }
        public string LinkUrl { get; set; }

        public string ImgUrl { get; set; }
    }
    /// <summary>
    /// 检索结果
    /// </summary>
    public class SearchResult
    {
        public SearchResult() {
            this.Items = new List<SearchResultItems>();
        }
        public List<SearchResultItems> Links { get; set; } = new List<SearchResultItems>();

        public List<SearchResultItems> Items { get; set; }

    }
    /// <summary>
    /// 影片的详情
    /// </summary>
    public class MovieInfo: MovieInfoReques
    {
        public string Des { get; set; }
        public Dictionary<string, List<MovieSourse>> Sourse { get; set; } = new Dictionary<string, List<MovieSourse>>();

    }
    public class MovieInfoReques: Title
    {

    }
    /// <summary>
    /// 影片资源链接
    /// </summary>
    public class MovieSourse
    {
        public string SourseUrl { get; set; }
        public string Title { get; set; }

    }
    /// <summary>
    /// 电影网站爬取基类
    /// </summary>
    public abstract class MovieSpiderBase
    {

        protected  IHttpHelper _Http;
        protected  IMovieDb sqliteWriter;

        public string StartUrl { get; set; }
        public abstract string Host { get; }
        public string Name { get; set; }
        protected readonly string _Path = @"D:\t\m";

        public string NextUrl { get; protected set; }
        public string Url { get; protected set; }
        public string Html { get; protected set; }
        public string PId { get; protected set; }
        public Items LastItem { get; protected set; }
        public MovieSpiderBase(string url, string name, IMovieDb writer, IHttpHelper http)
        {
            this.StartUrl = url;
            this.Name = name;
            this.sqliteWriter = writer;
            this.sqliteWriter.Name = this.Name;
            this._Http = http;
        }
        /// <summary>
        /// 开始爬取一个任务
        /// </summary>
        /// <param name="_down">是否下载电影视频文件</param>
        public void Start(bool _down = true)
        {
            var data = SelectTitle();
            if (data.Count == 0)
            {
                WriteTitle(new Title { Name = Name, StartUrl = StartUrl, SType = this.GetType().Name });
                data = SelectTitle();
            }
            PId = data.First().Id + "";
            LastItem = SelectLastItem();
            sqliteWriter.PId = PId;
            if (LastItem == null)
            {
                Url = StartUrl;
            }
            else
            {
                Url = LastItem.Url;
            }

            run(); // 抓取最新的记录
            if (_down) down(); // 下载未下载的记录

        }
        /// <summary>
        /// 站内搜索
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public abstract SearchResult Seach(string key, int n);
        /// <summary>
        /// 站内跳转
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public abstract MovieInfo To(MovieInfoReques url);
        /// <summary>
        /// 播放一个链接的视频
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public abstract Items Play(string url);

        /// <summary>
        /// 反射动态获取实例
        /// </summary>
        public static MovieSpiderBase GetMovieSpiderBase(string type, string url, string name, IMovieDb _Db, IHttpHelper _Http)
        {

            Assembly assem = Assembly.GetExecutingAssembly();
            var types = assem.GetTypes();//程序集下所有的类
            var t = types.Where(m => m.IsSubclassOf(typeof(MovieSpiderBase)))
                .FirstOrDefault(m => m.Name.ToLower() == type.ToLower());
            if (t != null)
                return Activator.CreateInstance(t, url, name, _Db, _Http) as MovieSpiderBase;
            return null;
        }

        /// <summary>
        /// 下载数据入口
        /// </summary>
        protected virtual void down()
        {
            var path = $"{_Path}\\{PId}_{Name}";
            if (!System.IO.Directory.Exists(path))
                System.IO.Directory.CreateDirectory(path);
            var data = SelectTitle();
            PId = data.First().Id + "";
            var items = SelectItems();
            foreach (var i in items)
            {
                // 判断是否下载完成，完成则跳过，否则继续下载
                var myPath = $"{path}\\{i.Name}";
                if (!System.IO.Directory.Exists(myPath))
                    System.IO.Directory.CreateDirectory(myPath);
                var myMurl = $"{myPath}\\index.m3u8";
                if (!System.IO.File.Exists(myMurl))
                {
                    if (!string.IsNullOrEmpty(i.MUrl))
                    {
                        _down(myPath, myMurl, i);
                    }
                }
            }

        }
        /// <summary>
        /// 下载数据方法
        /// </summary>
        /// <param name="myPath"></param>
        /// <param name="myMurl"></param>
        /// <param name="i"></param>
        protected abstract void _down(string myPath, string myMurl, Items i);
        /// <summary>
        /// 第一种下载数据的方法
        /// </summary>
        /// <param name="myPath"></param>
        /// <param name="myMurl"></param>
        /// <param name="i"></param>

        protected void down(string myPath, string myMurl, Items i)
        {
            // 下载主文件
            Html = _Http.GetHTMLByURLAsync(i.MUrl).GetAwaiter().GetResult();
            // 解析主文件，下载缺失部分
            if (string.IsNullOrEmpty(Html))
                return;
            var msgs = Html.Split("\n").ToList();
            if (msgs.Any(m => m.EndsWith(".ts")))
            {
                for (int j = 0; j < msgs.Count; j++)
                {
                    if (msgs[j].EndsWith(".ts"))
                    {
                        // 下载 并修改内容
                        var tsUrl = msgs[j];
                        var tsName = tsUrl.Split("/").Last();
                        msgs[j] = $"{myPath}\\{tsName}";
                        if (!System.IO.File.Exists(msgs[j]))
                        {
                            _Http.SavePhotoFromUrl(msgs[j], tsUrl);
                        }
                    }
                    if (msgs[j].StartsWith("#EXT-X-KEY"))
                    {
                        var keyText = msgs[j].Replace("，", ",").Split(",").First(m => m.StartsWith("URI="));
                        var KeyName = keyText.Split("\"")[1];
                        var keyPath = $"{myPath}\\{KeyName}";
                        if (!System.IO.File.Exists(keyPath))
                        {
                            var keyUrl = i.MUrl.Replace("index.m3u8", KeyName);
                            var key = _Http.GetHTMLByURLAsync(keyUrl).GetAwaiter().GetResult();
                            System.IO.File.WriteAllText(keyPath, key);
                        }
                    }
                }
                // 保存索引文件
                System.IO.File.WriteAllText(myMurl, string.Join("\n", msgs.Select(m => m.Replace(_Path, ""))));
            }

        }

        /// <summary>
        /// 先写索引文件
        /// 可以实现 边下边播
        /// </summary>
        /// <param name="myPath"></param>
        /// <param name="myMurl"></param>
        /// <param name="i"></param>
        public void down1(string myPath, Items i)
        {
            // 判断是否下载完成，
            var myMurl = $"{myPath}\\index.m3u8";
            var finish = $"{myPath}\\d.d";
            if (System.IO.File.Exists(finish))
                return;
            // 下载主文件
            Html = _Http.GetHTMLByURLAsync(i.MUrl).GetAwaiter().GetResult();
            // 解析主文件，下载缺失部分
            if (string.IsNullOrEmpty(Html))
                return;
            var msgs = Html.Split("\n").ToList();
            if (msgs.Any(m => m.EndsWith(".ts")))
            {
                // 保存索引文件
                var firstts = msgs.FirstOrDefault(m => m.EndsWith(".ts"));
                var tsName =  firstts.Split("/").Last();
                var rPath = firstts.Replace(tsName, "");
                if (!System.IO.File.Exists(myMurl))
                    System.IO.File.WriteAllText(myMurl, string.Join("\n", string.IsNullOrEmpty(rPath) ? msgs : msgs.Select(m => m.Replace(rPath, ""))));

                // 异步下载了

                Task.Factory.StartNew(()=> {
                    down(msgs, myPath, i.MUrl, finish);
                });  

            }
            else { 
                var newm3u8 = msgs.FirstOrDefault(m => m.ToLower().EndsWith(".m3u8"));
                if (newm3u8 != null)
                {
                    var newUrl = UrlReplace(i.MUrl,newm3u8);
                    i.MUrl = newUrl;
                    down1(myPath,i);
                }

            }

        }
        void down(List<string> msgs, string myPath, string MUrl, string finish)
        {

            Parallel.ForEach(msgs, new ParallelOptions { MaxDegreeOfParallelism = 4 }, m =>
             {
                 if (m.EndsWith(".ts"))
                 {
                    // 下载 并修改内容
                    var tsUrl = UrlReplace(MUrl, m);
                     var tsName = tsUrl.Split("/").Last();
                     var _path = $"{myPath}\\{tsName}";
                     if (!System.IO.File.Exists(_path))
                     {
                         _Http.SavePhotoFromUrlAsync(_path, tsUrl).Wait();
                     }
                 }
                 if (m.StartsWith("#EXT-X-KEY"))
                 {
                     var keyText = m.Replace("，", ",").Split(",").First(m => m.StartsWith("URI="));
                     var KeyName = keyText.Split("\"")[1];
                     var keyPath = $"{myPath}\\{KeyName}";
                     if (!System.IO.File.Exists(keyPath))
                     {
                         var keyUrl = UrlReplace(MUrl, KeyName);
                         var key = _Http.GetHTMLByURLAsync(keyUrl).GetAwaiter().GetResult();
                         System.IO.File.WriteAllText(keyPath, key);
                     }
                 }
             });

            System.IO.File.WriteAllText(finish, "");
        }

        string UrlReplace(string url, string newName)
        {
            if (newName.ToLower().StartsWith("http"))
                return newName;
            var oldName = url.Split("/").Last();
            return url.Replace(oldName, newName);
        }

        void run()
        {
            _run();
            while (!string.IsNullOrEmpty(NextUrl))
            {
                Url = NextUrl;
                _run();
            }

        }
        /// <summary>
        /// 抓取并解析网页方法
        /// </summary>
        protected abstract void _run();

        public List<Title> SelectTitle()
        {
            try
            {
                return sqliteWriter.SelectTitle();
            }
            catch (Exception ex)
            {
                return new List<Title>();
            }

        }
        public string GetUrl(string url) {
            return (url + "").ToLower().StartsWith("http") ? url : this.Host + "/" + url;
        }
        public List<Items> SelectItems()
        {
            return sqliteWriter.SelectItems(this.PId);
        }
        public Items SelectLastItem()
        {
            return sqliteWriter.SelectLastItem();
        }
        public void WriteTitle(Title t)
        {
            sqliteWriter.WriteTitle(t);
        }
        public void WriteItems(List<Items> t)
        {
            sqliteWriter.WriteItems(t);
        }
    }
}
